<?php
require_once APPLICATION_PATH.'/controllers/AbstractBaseController.class.php';
require_once APPLICATION_PATH.'/utils/StoreUtils.php';
require_once APPLICATION_PATH.'/utils/DataRenderer.class.php';
require_once MORIARTY_DIR.'store.class.php';
require_once MORIARTY_DIR.'changeset.class.php';

class StoreController extends AbstractBaseController
{
    const QUERY_DESCRIBE = 'describe';
    const QUERY_SELECT = 'select';
    const QUERY_ASK = 'ask';
    const QUERY_CONSTRUCT = 'construct';

    private $storeUri;
    private $storeUtils;

    public function init()
    {
        $storeUri = $this->getSessionManager()->getCurrentStore();

        $this->storeUri = $storeUri;
        $this->view->pageHeader = 'Store: '.$storeUri.'&nbsp;[<a href="/store/switch">change</a>]';
        $this->view->storeUri = $storeUri;

        $this->view->curlVersion = $this->getCurlVersion();
    }

    public function indexAction()
    {
        $this->getStoreUtils();

        $msg = @$_REQUEST['msg'];

        if (!empty($msg))
        {
            $this->view->flashMessage = $msg;
            $this->view->flashMessageClass = 'msgInfo';
        }
    }

    public function authenticateAction()
    {
        if ($this->_request->isPost())
        {
            $user = $_POST['user'];
            $password = $_POST['password'];

            $key = base64_encode($this->storeUri);

            $this->getSessionManager()->set($key, $user.','.$password);

            if (isset($_POST['referrer']))
            {
                $this->_redirect($_POST['referrer']);
            }
            else
            {
                $this->_redirect('/store');
            }
        }
    }

    public function switchAction()
    {
        $this->getSessionManager()->resetCurrentStore();
        $this->_redirect('/');
    }

    public function searchAction()
    {
        $q = @$_GET['q'];

        $this->view->q = $q;

        if (!empty($q))
        {
            $this->view->resourceList = $this->getStoreUtils()->search($q);
        }
        else
        {
            $this->view->resourceList = new ResourceList();
            $this->view->resourceList->items = array();
        }
    }

    public function clearAction()
    {
        if ($this->_request->isPost())
        {
            if (isset($_POST['confirm']))
            {
                $jobUri = $this->getStoreUtils(true)->clearStore();
            }

            $this->_redirect('/store');
        }
        else
        {
            $this->getStoreUtils(true);
        }
    }

    public function reindexAction()
    {
        if ($this->_request->isPost())
        {
            if (isset($_POST['confirm']))
            {
                $jobUri = $this->getStoreUtils(true)->reindexStore();
            }

            $this->_redirect('/store?msg='.urlencode('Re-indexing store: job = '.$jobUri));
        }
        else
        {
            $this->getStoreUtils(true);
        }
    }

    public function snapshotsAction()
    {
        $snapshotUris = $this->getStoreUtils(true)->getSnapshots();
        $this->view->snapshots = $snapshotUris;
    }

    public function snapshotAction()
    {
        if ($this->_request->isPost())
        {
            if (isset($_POST['confirm']))
            {
                $jobUri = $this->getStoreUtils(true)->snapshot();
            }

            $this->_redirect('/store?msg='.urlencode('Taking snapshot of store: job = '.$jobUri));
        }
    }

    public function restoreAction()
    {
        $snapshotUri = @$_REQUEST['snapshotUri'];

        if ($this->_request->isPost())
        {
            if (isset($_POST['confirm']))
            {
                $jobUri = $this->getStoreUtils(true)->restore($snapshotUri);
            }

            $this->_redirect('/store?msg='.urlencode('Restoring snapshot of store: job = '.$jobUri));
        }
        else
        {
            $this->view->snapshotUri = $snapshotUri;
        }
    }

    public function fpmapAction()
    {
        if ($this->_request->isPost())
        {
            if (isset($_REQUEST['update']))
            {
                $newFpMap = $_REQUEST['fpmap'];
                $location = $_REQUEST['location'];

                $response = $this->getStoreUtils(true)->putToLocation($location, $newFpMap);

                if ($response->is_success())
                {
                    $this->view->flashMessage = 'Field Predicate Map updated successfully';
                    $this->view->flashMessageClass = 'msgSuccess';
                }
                else
                {
                    $this->view->flashMessage = 'ERROR: '.$response->body;
                    $this->view->flashMessageClass = 'msgError';
                }
            }
        }

        $data = $this->getStoreUtils(true)->getFPMap();

        $this->view->location = $data['location'];
        $this->view->data = $data['data'];
    }

    public function queryprofileAction()
    {
        if ($this->_request->isPost())
        {
            if (isset($_REQUEST['update']))
            {
                $queryProfile = $_REQUEST['queryprofile'];
                $location = $_REQUEST['location'];

                $response = $this->getStoreUtils(true)->putToLocation($location, $queryProfile);

                if ($response->is_success())
                {
                    $this->view->flashMessage = 'Query Profile updated successfully';
                    $this->view->flashMessageClass = 'msgSuccess';
                }
                else
                {
                    $this->view->flashMessage = 'ERROR: '.$response->body;
                    $this->view->flashMessageClass = 'msgError';
                }
            }
        }

        $data = $this->getStoreUtils(true)->getQueryProfile();

        $this->view->location = $data['location'];
        $this->view->data = $data['data'];
    }

    public function statusAction()
    {
        $readStatus = $this->getStoreUtils()->getReadStatus();

        if (empty($readStatus))
        {
            $readStatus = 'read/write';
        }

        $this->view->statusMessage = $readStatus;
    }

    public function typesAction()
    {
        $pageLength = 20;
        
        $typeUri = @$_GET['type'];
        
        if (!empty($typeUri))
        {
            if (isset($_GET['offset']))
            {
                $offset = $_GET['offset'];
            }
            else
            {
                $offset = 0;
            }
            
            $offset = @$_GET['offset'];
            
            $searchString = 'type:'.str_replace(':', '\:', $typeUri);
            $searchResults = $this->getStoreUtils()->search($searchString, $offset, $pageLength);
            $this->view->resourceList = $searchResults;
            $this->view->typeUri = $typeUri;
        }
        else
        {
            $selectData = $this->getStoreUtils()->getDistinctTypes();
            $types = array();
            foreach ($selectData as $type)
            {
                $typeUri = $type['type']['value'];
                if (!empty($typeUri))
                {
                    $types[] = $typeUri;
                }
            }
            sort($types);

            $this->view->types = $types;
        }
    }

    public function typecountAction()
    {
        $this->disableLayout();

        $type = $_GET['type'];
        $typeCount = $this->getStoreUtils()->getTypeCount($type);

        echo $typeCount;
    }

    public function jobsAction()
    {
        $this->disableLayout();

        $jobUris = $this->getStoreUtils()->getJobQueueUris();

        if (count($jobUris) == 0)
        {
            echo 'No jobs';
        }
        else
        {
            foreach($jobUris as $jobUri)
            {
                echo "{$jobUri}<br/>";
            }
        }
    }

    public function browseAction()
    {
        $this->view->uri = @$_GET['uri'];

        if (!empty($this->view->uri))
        {
            $rdfXML = $this->getStoreUtils()->describe($this->view->uri);
            $graph = new SimpleGraph($rdfXML);
            $this->view->data = $graph;
            $this->view->dataRenderer = new DataRenderer();
        }
    }

    public function addtripleAction()
    {
        if ($this->_request->isPost())
        {
            $s = @$_REQUEST['s'];
            $p = @$_REQUEST['p'];
            $o = @$_REQUEST['o'];
            $type = @$_REQUEST['type'];

            $oldGraph = new SimpleGraph();
            $newGraph = new SimpleGraph();

            if ($type == 'uri')
            {
                $newGraph->add_resource_triple($s, $p, $o);
            }
            else
            {
                $newGraph->add_literal_triple($s, $p, $o);
            }

            $cs = new ChangeSet(array(
        		'before'=>$oldGraph->get_index(),
        		'after'=>$newGraph->get_index(),
            ));

            $this->getStoreUtils(true)->applyChangeset($cs);

            $this->_redirect('/store/browse?uri='.urlencode($s));
        }
        else
        {
            $this->getStoreUtils(true);

            if (isset($_REQUEST['s']))
            {
                $this->view->s = $_REQUEST['s'];
            }
        }
    }

    public function deleteAction()
    {
        //TODO Add confirmation for delete.

        $type = $_REQUEST['type'];
        $s = $_REQUEST['s'];
        $p = $_REQUEST['p'];
        $o = $_REQUEST['o'];

        $storeUtils = $this->getStoreUtils();
        $rdf = $storeUtils->describe($s);

        $oldGraph = new SimpleGraph($rdf);
        $newGraph = new SimpleGraph($rdf);

        if ($type == 'literal')
        {
            $newGraph->remove_literal_triple($s, $p, $o);
        }
        else
        {
            $newGraph->remove_resource_triple($s, $p, $o);
        }

        $cs = new ChangeSet(array(
            'before'=>$oldGraph->get_index(),
            'after'=>$newGraph->get_index(),
        ));

        $this->getStoreUtils(true)->applyChangeset($cs);

        $this->_redirect('/store/browse?uri='.urlencode($s));
    }

    public function editAction()
    {
        $request = $this->getRequest();

        if ($request->isPost())
        {
            $oldGraph = new SimpleGraph();
            $newGraph = new SimpleGraph();

            if ($_REQUEST['old_type'] == 'literal')
            {
                $oldGraph->add_literal_triple($_REQUEST['old_s'], $_REQUEST['old_p'], $_REQUEST['old_o']);
            }
            else
            {
                $oldGraph->add_resource_triple($_REQUEST['old_s'], $_REQUEST['old_p'], $_REQUEST['old_o']);
            }

            if ($_REQUEST['type'] == 'literal')
            {
                $newGraph->add_literal_triple($_REQUEST['s'], $_REQUEST['p'], $_REQUEST['o']);
            }
            else
            {
                $newGraph->add_resource_triple($_REQUEST['s'], $_REQUEST['p'], $_REQUEST['o']);
            }

            $cs = new ChangeSet(array(
        		'before'=>$oldGraph->get_index(),
        		'after'=>$newGraph->get_index(),
            ));

            $this->getStoreUtils(true)->applyChangeset($cs);

            $this->_redirect('/store/browse?uri='.urlencode($_REQUEST['s']));
        }
        else
        {
            $this->getStoreUtils(true);

            $this->view->s = $request->getParam('s');
            $this->view->p = $request->getParam('p');
            $this->view->o = $request->getParam('o');
            $this->view->type = $request->getParam('type');
        }
    }

    public function queryAction()
    {
        $query = $this->getRequest()->getParam('query');
        $queryType = $this->getQueryType($query);

        if (!empty($query))
        {
            $this->getSessionManager()->addRecentQuery($query);
            $this->view->query = $query;

            try
            {
                switch ($queryType)
                {
                    case self::QUERY_SELECT:
                        $data = $this->getStoreUtils()->select($query);
                        break;
                    case self::QUERY_DESCRIBE:
                    case self::QUERY_ASK:
                    case self::QUERY_CONSTRUCT:
                        $rdfXML = $this->getStoreUtils()->query($query);
                        $data = new SimpleGraph($rdfXML);
                        break;
                }

                if (!empty($data))
                {
                    $this->view->data = $data;
                }

                $this->view->dataRenderer = new DataRenderer();
            }
            catch (Zend_Exception $e)
            {
                if ($e->getCode() == '400')
                {
                    $this->view->data == null;
                    $this->view->flashMessage = 'Invalid query';
                    $this->view->flashMessageClass = 'msgError';
                }
            }
        }

        $this->view->recentQueries = $this->getSessionManager()->getRecentQueries();
    }

    public function uploadAction()
    {
        $form = new Zend_Form();
        $form->setName('upload-data');
        $form->setAttrib('enctype', 'multipart/form-data');

        $uploadElement = new Zend_Form_Element_File('upload');
        $uploadElement->setLabel('File to upload');
        $form->addElement($uploadElement, 'upload');

        $submit = new Zend_Form_Element_Submit('submit');
        $submit->setLabel('Upload File');
        $form->addElement($submit, 'submit');

        if ($this->_request->isPost())
        {
            $formData = $this->_request->getPost();
            if ($form->isValid($formData))
            {
                $form->upload->receive();
                $uploadedFileLocation = $form->upload->getFileName();

                $this->getStoreUtils()->importFile($uploadedFileLocation);

                $this->view->flashMessage = 'File uploaded successfully';
                $this->view->flashMessageClass = 'msgSuccess';
            }
            else
            {
                $form->populate($formData);
            }
        }

        $this->view->form = $form;
    }

    /**
     * Get the StoreUtils object for performing operations on a store.  This will prompt for credentials, if required.
     */
    private function getStoreUtils($protectedAction=false)
    {
        try
        {
            if (!isset($this->storeUtils))
            {
                $key = base64_encode($this->storeUri);
                $credentials = $this->getSessionManager()->get($key);
                if (!empty($credentials))
                {
                    $credentials = explode(',', $credentials);
                    $user = $credentials[0];
                    $password = $credentials[1];
                }
                else
                {
                    if ($protectedAction)
                    {
                        throw new StoreAuthenticationException('Requires authentication');
                    }
                    $user = null;
                    $password = null;
                }

                $this->storeUtils = new StoreUtils($this->storeUri, $user, $password);
            }
        }
        catch (InvalidStoreException $e)
        {
            $this->getSessionManager()->resetCurrentStore();
            $this->_redirect('/?invalidStore');
        }
        catch (StoreAuthenticationException $e)
        {
            $referrer = $_SERVER['REQUEST_URI'];
            $this->_redirect('/store/authenticate?referrer='.$referrer);
            die;
        }

        return $this->storeUtils;
    }

    private function getQueryType($query)
    {
        if (empty($query))
        {
            return;
        }

        if (stripos($query, self::QUERY_DESCRIBE) !== FALSE)
        {
            return self::QUERY_DESCRIBE;
        }
        if (stripos($query, self::QUERY_SELECT) !== FALSE)
        {
            return self::QUERY_SELECT;
        }
        if (stripos($query, self::QUERY_ASK) !== FALSE)
        {
            return self::QUERY_ASK;
        }
        if (stripos($query, self::QUERY_CONSTRUCT) !== FALSE)
        {
            return self::QUERY_CONSTRUCT;
        }
    }

    private function getCurlVersion()
    {
        $arrDetails = curl_version();
        return $arrDetails['version'];
    }
}
